package top.whysu.gps.service.navigation.impl;

import liquibase.pro.packaged.E;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.Na;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import top.whysu.gps.constant.CodeConstant;
import top.whysu.gps.constant.Constant;
import top.whysu.gps.constant.DataDictionary;
import top.whysu.gps.constant.SystemConfigKey;
import top.whysu.gps.dao.gps.NavigationDao;
import top.whysu.gps.dao.gps.NavigationRecordDao;
import top.whysu.gps.dao.gps.RouteCurrentDao;
import top.whysu.gps.dto.navigation.*;
import top.whysu.gps.dto.page.PagenationDataDTO;
import top.whysu.gps.exception.BusinessException;
import top.whysu.gps.po.navigation.NavigationPO;
import top.whysu.gps.po.navigation.NavigationRecordPO;
import top.whysu.gps.po.navigation.RouteCurrentPO;
import top.whysu.gps.service.navigation.NavigationService;
import top.whysu.gps.service.navigation.RouteCurrentService;
import top.whysu.gps.service.system.AccountService;
import top.whysu.gps.service.system.SystemConfigService;
import top.whysu.gps.utils.CalculationUtil;
import top.whysu.gps.utils.DateUtils;
import top.whysu.gps.utils.ExcelUtil;
import top.whysu.gps.utils.PageUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 航行管理
 */
@Service("NavigationServiceImpl")
public class NavigationServiceImpl implements NavigationService {
    private static final Logger LOGGER = LoggerFactory.getLogger(NavigationServiceImpl.class);

    @Resource
    private SystemConfigService systemConfigService;
    @Resource
    private NavigationDao navigationDao;
    @Resource
    private NavigationRecordDao navigationRecordDao;
    @Resource
    private AccountService accountService;
    @Resource
    private RouteCurrentDao routeCurrentDao;
    @Resource
    private RouteCurrentService routeCurrentService;


    @Override
    public synchronized void wholeSwitchMode(SwitchModeDTO switchModeDTO) {
        // 必填参数需要校验
        checkConfig(switchModeDTO.getMode());
        // 运行状态
        Integer modeStatus = switchModeDTO.getModeStatus();
        // 当前操作用户以及时间
        String currentUser = accountService.getCurrentLoginUser();
        Date currentDate = new Date();
        // 从缓存中获取当前航程
        BigDecimal currentNavigationId = getCurrentNavigationId();
        // 如果是要结束航程
        if (modeStatus == DataDictionary.ModeStatus.STOP) {
            // 如果有航程的话，则更新结束时间
            if (currentNavigationId != null) {
                NavigationPO navigationPO = new NavigationPO();
                navigationPO.setId(currentNavigationId);
                navigationPO.setEndTime(currentDate);
                navigationPO.setUpdateBy(currentUser);
                navigationPO.setUpdateTime(currentDate);
                // 查询当前航线总数
                navigationPO.setTotalNumber(navigationRecordDao.countByNavigationId(currentNavigationId));
                navigationDao.update(navigationPO);
                // 将航线表的状态都置为等待发送
                String resetSwitch = systemConfigService.getConfig(SystemConfigKey.Common.COMMON_RESET_SEND_STATUS);
                if (StringUtils.isNotBlank(resetSwitch) && String.valueOf(DataDictionary.YesOrNo.YES).equals(resetSwitch)) {
                    routeCurrentDao.updateAllRouteState(DataDictionary.RouteState.WAIT_FOR_SEND);
                }
                // 同时将当前航行表id置为空
                systemConfigService.newTxUpdateConfig(SystemConfigKey.CURRENT_NAVIGATION_ID, "");
            }
        } else if (currentNavigationId == null) {
            // 判断航线表是否为空
            long totalRouteSize = routeCurrentDao.count(null);
            if (totalRouteSize == 0) {
                throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "请先配置航线表！");
            }
            // 缓存中不存在的话，则创建新航线
            NavigationPO navigationPO = new NavigationPO();
            navigationPO.setNavigationName("航程" + formatDate(currentDate));
            navigationPO.setStartTime(currentDate);
            navigationPO.setCreateBy(currentUser);
            navigationPO.setCreateTime(currentDate);
            navigationPO.setUpdateBy(currentUser);
            navigationPO.setUpdateTime(currentDate);
            navigationDao.insert(navigationPO);
            if (navigationPO.getId() == null) {
                throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "航程创建失败！");
            }
            currentNavigationId = navigationPO.getId();
            // 更新当前航行日程表
            systemConfigService.newTxUpdateConfig(SystemConfigKey.CURRENT_NAVIGATION_ID, currentNavigationId == null ? "" : String.valueOf(currentNavigationId));
        }
        // 更新配置
        systemConfigService.newTxUpdateConfig(SystemConfigKey.CURRENT_MODE, String.valueOf(switchModeDTO.getMode()));
        systemConfigService.newTxUpdateConfig(SystemConfigKey.CURRENT_MODE_STATUS, String.valueOf(switchModeDTO.getModeStatus()));
        // 是否处于手动模式
        if (switchModeDTO.getByHand() != null) {
            systemConfigService.newTxUpdateConfig(SystemConfigKey.Common.BY_HAND, String.valueOf(switchModeDTO.getByHand()));
        }
    }


    @Override
    public void importRouteExcel(MultipartFile uploadFile) {
        List<RouteImportExcelDTO> dtoList;
        try {
            dtoList = ExcelUtil.importRouteExcel(uploadFile);
        } catch (BusinessException e) {
            LOGGER.error("文件导入业务异常", e);
            throw new BusinessException(e.getCode(), e.getMessage());
        } catch (Exception e) {
            LOGGER.error("文件导入系统异常", e);
            throw new BusinessException(CodeConstant.Http.EXECUTE_FAIL, "文件导入异常");
        }
        // 当前操作用户
        String currentUser = accountService.getCurrentLoginUser();
        // 时间
        Date currentDate = new Date();
        // 进行list数据转换
        List<RouteCurrentPO> currentPOList = new ArrayList<>();
        dtoList.forEach(it -> {
            RouteCurrentPO currentPO = new RouteCurrentPO();
            BeanUtils.copyProperties(it, currentPO);
            currentPO.setId(null);
            currentPO.setRouteState(DataDictionary.RouteState.WAIT_FOR_SEND);
            currentPO.setCreateTime(currentDate);
            currentPO.setCreateBy(currentUser);
            currentPO.setCreateTime(currentDate);
            currentPO.setUpdateBy(currentUser);
            currentPO.setUpdateTime(currentDate);
            // 速度如果为空，则置为0
            if (StringUtils.isBlank(it.getSpeed())) {
                currentPO.setSpeed("0");
            }
            currentPOList.add(currentPO);
            // 避免内存泄漏，先处理100条
            if (currentPOList.size() > 100) {
                routeCurrentService.batchInsert(currentPOList);
                currentPOList.clear();
            }
        });
        // 剩下的处理
        if (CollectionUtils.isNotEmpty(currentPOList)) {
            routeCurrentService.batchInsert(currentPOList);
        }
    }

    @Override
    public PagenationDataDTO<NavigationPO> getNavigationPage(NavigationPageQueryDTO queryDTO) {
        // 开始时间，加上后缀00:00:00(这里有空格)
        if (queryDTO.getQueryStartTime() != null) {
            queryDTO.setQueryStartTime(DateUtils.parseDate(
                    DateUtils.formatDate(queryDTO.getQueryStartTime(), "yyyy-MM-dd") + " 00:00:00"));
        }
        // 结束时间，加上后缀23:59:59(这里有空格)
        if (queryDTO.getQueryEndTime() != null) {
            queryDTO.setQueryEndTime(DateUtils.parseDate(
                    DateUtils.formatDate(queryDTO.getQueryEndTime(), "yyyy-MM-dd") + " 23:59:59"));
        }
        // 获取集合
        Map queryMap = objectToMap(queryDTO);
        // limit的起始值
        queryMap.put("rowIndex", PageUtils.getRowIndex(queryDTO.getPageNum(), queryDTO.getPageSize()));
        List<NavigationPO> list = navigationDao.queryAllByLimit(queryMap);
        // 获取总数
        long totalSize = navigationDao.count(queryMap);
        if (CollectionUtils.isNotEmpty(list) && totalSize > 0) {
            return new PagenationDataDTO<>(totalSize, queryDTO.getPageNum(), queryDTO.getPageSize(), list);
        } else {
            return PageUtils.toPagedResult(null);
        }
    }

    @Override
    public PagenationDataDTO<NavigationRecordPO> getNavigationRecordPage(NavigationRecordPageQueryDTO queryDTO) {
        // 获取集合
        Map queryMap = objectToMap(queryDTO);
        // limit的起始值
        queryMap.put("rowIndex", PageUtils.getRowIndex(queryDTO.getPageNum(), queryDTO.getPageSize()));
        List<NavigationRecordPO> list = navigationRecordDao.queryAllByLimit(queryMap);
        // 获取总数
        long totalSize = navigationRecordDao.count(queryMap);
        if (CollectionUtils.isNotEmpty(list) && totalSize > 0) {
            return new PagenationDataDTO<>(totalSize, queryDTO.getPageNum(), queryDTO.getPageSize(), list);
        } else {
            return PageUtils.toPagedResult(null);
        }
    }

    @Override
    public PagenationDataDTO<NavigationPointMapDTO> getNavigationPointMap(NavigationRecordPageQueryDTO queryDTO) {
        // 先分页查询记录
        PagenationDataDTO<NavigationRecordPO> pageRecordResult = this.getNavigationRecordPage(queryDTO);
        // 集合
        List<NavigationRecordPO> recordList = pageRecordResult.getList();
        if (CollectionUtils.isNotEmpty(recordList)) {
            List<NavigationPointMapDTO> pointMapList = new ArrayList<>();
            // 遍历进行解析
            recordList.forEach(it -> {
                NavigationPointMapDTO mapDTO = new NavigationPointMapDTO();
                if (it.getCreateTime() != null) {
                    mapDTO.setCreateTime(DateUtils.formatDateTime(it.getCreateTime()));
                }
                mapDTO.setSpeed(it.getSpeed());
                mapDTO.setMode(it.getMode());
                mapDTO.setCourse(it.getCourse());
                if (StringUtils.isNotBlank(it.getLatitude()) || StringUtils.isNotBlank(it.getLongitude())) {
                    // 经度
                    Double longitude = CalculationUtil.transformPos(it.getLongitude());
                    if (Constant.Longitude.WEST.equals(it.getLongitudeType())) {
                        longitude = -longitude;
                    }
                    mapDTO.setLongitude(longitude);
                    // 纬度
                    Double latitude = CalculationUtil.transformPos(it.getLatitude());
                    if (Constant.Latitude.SOUTH.equals(it.getLatitudeType())) {
                        latitude = -latitude;
                    }
                    mapDTO.setLatitude(latitude);
                }
                pointMapList.add(mapDTO);
            });
            // 返回
            return new PagenationDataDTO<>(pageRecordResult.getTotalSize(), pageRecordResult.getPageNum(), pageRecordResult.getPageSize(), pointMapList);
        }
        return PageUtils.toPagedResult(null);
    }

    /**
     * 获取当前航行日程表
     */
    private BigDecimal getCurrentNavigationId() {
        String value = systemConfigService.getConfig(SystemConfigKey.CURRENT_NAVIGATION_ID);
        if (StringUtils.isNotBlank(value)) {
            try {
                return new BigDecimal(value);
            } catch (Exception e) {
                LOGGER.error("获取当前航行日程表异常，value={}", value);
            }
        }
        return null;
    }

    public static String formatDate(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_HHmm");
        return formatter.format(date);
    }

    private static Map<String, Object> objectToMap(Object obj) {
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(obj));
            }
        } catch (Exception e) {
            LOGGER.error("基类转换异常", e);
        }
        try {
            // 父类
            Field[] fatherDeclaredFields = obj.getClass().getSuperclass().getDeclaredFields();
            for (Field field : fatherDeclaredFields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(obj));
            }
        } catch (Exception e) {
            LOGGER.error("父类转换异常", e);
        }

        return map;
    }

    @Override
    public void checkConfig(Integer mode) {
        // 公共参数
        if (StringUtils.isBlank(getConfig(SystemConfigKey.Common.COMMON_SEND_TIMING))) {
            throw new BusinessException(CodeConstant.Http.INFO_ERROR, "公共参数：【串口发送间隔时间】未设置！");
        }
        // 航行模式
        if (DataDictionary.Mode.NAVIGATION == mode) {
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Navigation.SPEED_SCOPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "航行模式：【航速误差范围】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Navigation.COURSE_SCOPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "航行模式：【航向误差方位】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Navigation.FINISH_MODE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "航行模式：【发送完成后默认切换xx模式】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Navigation.DEGREE_SCOPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "航行模式：【经纬度误差范围】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Navigation.DEFAULT_SPEED))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "航行模式：【默认速度】未设置！");
            }
        }
        // 漂浮模式
        if (DataDictionary.Mode.WANDER == mode) {
            String startLongitudeType = getConfig(SystemConfigKey.Wander.START_LONGITUDE_TYPE);
            String startLongitude = getConfig(SystemConfigKey.Wander.START_LONGITUDE);
            String startLatitudeType = getConfig(SystemConfigKey.Wander.START_LATITUDE_TYPE);
            String startLatitude = getConfig(SystemConfigKey.Wander.START_LATITUDE);
            if (StringUtils.isBlank(startLongitudeType) || StringUtils.isBlank(startLongitude)
                    || StringUtils.isBlank(startLatitudeType) || StringUtils.isBlank(startLatitude)) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "漂浮模式：【漂浮范围起点坐标】未设置！");
            }
            String endLongitudeType = getConfig(SystemConfigKey.Wander.END_LONGITUDE_TYPE);
            String endLongitude = getConfig(SystemConfigKey.Wander.END_LONGITUDE);
            String endLatitudeType = getConfig(SystemConfigKey.Wander.END_LATITUDE_TYPE);
            String endLatitude = getConfig(SystemConfigKey.Wander.END_LATITUDE);
            if (StringUtils.isBlank(endLongitudeType) || StringUtils.isBlank(endLongitude)
                    || StringUtils.isBlank(endLatitudeType) || StringUtils.isBlank(endLatitude)) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "漂浮模式：【漂浮范围终点坐标】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Wander.WANDER_SPEED_SCOPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "漂浮模式：【速度范围】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Wander.WANDER_SPEED_ERROR_SCOPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "漂浮模式：【速度误差范围】未设置！");
            }
        }
        // 锚泊模式
        if (DataDictionary.Mode.ANCHOR == mode) {
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Anchor.ANCHOR_TYPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "锚泊模式: 【锚停类别】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Anchor.ANCHOR_ROTATION_TYPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "锚泊模式: 【旋转方向】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Anchor.ANCHOR_RADIUS))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "锚泊模式: 【半径大小】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Anchor.ANCHOR_RADIUS_SCOPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "锚泊模式: 【误差半径范围】未设置！");
            }
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Anchor.ANCHOR_TURN_AROUND_TIME))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "锚泊模式: 【转一圈360°需要多少时间】未设置！");
            }
        }
        // 单点循环
        if (DataDictionary.Mode.SINGLE_CIRCLE == mode) {
            if (StringUtils.isBlank(getConfig(SystemConfigKey.Single.SPEED_SCOPE))) {
                throw new BusinessException(CodeConstant.Http.INFO_ERROR, "单点循环：【航速误差范围】未设置！");
            }
        }
    }

    @Override
    public void batchDeleteNavigationRecord(BatchDeleteNavigationDTO dto) {
        List<BigDecimal> navigationIdList = dto.getNavigationIdList();
        if (CollectionUtils.isEmpty(navigationIdList)) {
            throw new BusinessException(CodeConstant.Http.INFO_ERROR, "请指定需要删除的记录！");
        }
        // 删除航行记录
        navigationRecordDao.batchDeleteByNavigationIdList(navigationIdList);
        // 删除航程记录
        navigationDao.batchDeleteByIdList(navigationIdList);
    }

    private String getConfig(String systemConfigKey) {
        return systemConfigService.getConfig(systemConfigKey);
    }

}